#include <stdio.h>
#include <termios.h> 
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>

int serial_fd;                  //串口驱动文件描述符
int file_fd;
unsigned char read_buffer[512] = {0};


//打开串口设备
int open_serial_port(void)
{
    char error_info[256] = {0};

    serial_fd = open("/dev/ttyS1", O_RDWR);
    if (serial_fd == -1) {
        perror(error_info);
        return -1;
    }
    return 0;
}
//设置串口参数
int set_serial_attribs(void)
{

    struct  termios opt;          //配置串口的属性定义在结构体struct termios中
    tcgetattr(serial_fd, & opt);         //获取终端控制属性
 
    cfsetispeed(& opt, B921600);  //指定输入波特率(若不设置系统默认9600bps)
    cfsetospeed(& opt, B921600);  //指定输出波特率(若不设置系统默认9600bps)
 
    /* c_lflag 本地模式 */
    opt.c_cflag &= ~ INPCK;           //不启用输入奇偶检测
    opt.c_cflag |=  (CLOCAL |  CREAD); //CLOCAL忽略 modem 控制线,CREAD打开接受者
 
    /* c_lflag 本地模式 */
    opt.c_lflag &= ~(ICANON | ECHO | ECHOE |  ISIG); //ICANON启用标准模式;ECHO回显输入字符;ECHOE如果同时设置了 ICANON，字符 ERASE 擦除前一个输入字符，WERASE 擦除前一个词;ISIG当接受到字符 INTR, QUIT, SUSP, 或 DSUSP 时，产生相应的信号
 
    /* c_oflag 输出模式 */
    opt.c_oflag &= ~ OPOST;             //OPOST启用具体实现自行定义的输出处理
    opt.c_oflag &= ~(ONLCR | OCRNL);    //ONLCR将输出中的新行符映射为回车-换行,OCRNL将输出中的回车映射为新行符
 
    /* c_iflag 输入模式 */
    opt.c_iflag &= ~(BRKINT | INPCK | ISTRIP);
    opt.c_iflag &= ~(ICRNL | INLCR);          	//ICRNL将输入中的回车翻译为新行 (除非设置了 IGNCR),INLCR将输入中的 NL 翻译为 CR
    opt.c_iflag &= ~(IXON | IXOFF | IXANY);    	//IXON启用输出的 XON/XOFF流控制,IXOFF启用输入的 XON/XOFF流控制,IXANY(不属于 POSIX.1；XSI) 允许任何字符来重新开始输出
 
    /* c_cflag 控制模式 */
    opt.c_cflag &= ~ CSIZE;     //字符长度掩码,取值为 CS5, CS6, CS7, 或 CS8,加~就是无
    opt.c_cflag |=   CS8;       //数据宽度是8bit
    opt.c_cflag &= ~ CSTOPB;    //CSTOPB设置两个停止位，而不是一个,加~就是设置一个停止位
    opt.c_cflag &= ~ PARENB;    //PARENB允许输出产生奇偶信息以及输入的奇偶校验,加~就是无校验
 
    /* c_cc[NCCS] 控制字符 */
    opt.c_cc[VTIME] = 0 ;   //等待数据时间(10秒的倍数),每个单位是0.1秒  若20就是2秒
    opt.c_cc[VMIN] = 0 ;    //最少可读数据,非规范模式读取时的最小字符数，设为0则为非阻塞，如果设为其它值则阻塞，直到读到到对应的数据,就像一个阀值一样，比如设为8，如果只接收到3个数据，那么它是不会返回的，只有凑齐8个数据后一齐才READ返回，阻塞在那儿
    /* new_cfg.c_cc[VMIN]   =   8;//DATA_LEN;
       new_cfg.c_cc[VTIME]  =   20;//每个单位是0.1秒  20就是2秒
       如果这样设置，就完全阻塞了，只有串口收到至少8个数据才会对READ立即返回，或才少于8个数据时，超时2秒也会有返回
       另外特别注意的是当设置VTIME后，如果read第三个参数小于VMIN ，将会将VMIN 修改为read的第三个参数*/
 
    /*TCIFLUSH  刷清输入队列
      TCOFLUSH  刷清输出队列
      TCIOFLUSH 刷清输入、输出队列*/
    tcflush(serial_fd, TCIOFLUSH);         //刷串口清缓存
    tcsetattr(serial_fd, TCSANOW, &opt);   //设置终端控制属性,TCSANOW：不等数据传输完毕就立即改变属性

    return 0;
}


int main()
{
	FILE *file;
	fd_set readfds;
	int retval;
	int r_len = 0;
	int w_len = 0;
	static int count = 0;


	open_serial_port();
	set_serial_attribs();


	file = fopen("audio_log.DAT", "w");
	//file_fd = open("audio_log.DAT", O_WRONLY, O_APPEND | O_CREAT | O_NONBLOCK | O_TRUNC);

	
	
	while(1)
	{
		// 清空文件描述符集合
		FD_ZERO(&readfds);
		// 将串口文件描述符添加到集合中
		FD_SET(serial_fd, &readfds);
		
		// 等待数据，无超时
		retval = select(serial_fd + 1, &readfds, NULL, NULL, NULL);
		if (retval == -1) {
			perror("select()");
			break;
		} 
		
		if(FD_ISSET(serial_fd, &readfds) == 1)
		{
			// 数据可读
			r_len = read(serial_fd, read_buffer, sizeof(read_buffer));
			if (r_len < 0) {
			    perror("read");
			    close(serial_fd);
			}
			
			// 写入文件
			w_len = fwrite(read_buffer, 1, r_len, file);
			count += w_len;
			//count += r_len;
			if(w_len)
			{
				printf("读取数据长度%d, 写入数据长度%d，总长度%d\n", r_len, w_len, count);
			}
		}
	}
	close(serial_fd);
	fclose(file);
	return 0;
}
